package com.smartian.dnshosts;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetAddress;
import java.net.UnknownHostException;

public class DnsInetAddress implements InvocationHandler {

    private DnsHostsManager mDnsResoverManager;
    private RealInvocationHandler mInvocationHandler;

    public DnsInetAddress(DnsHostsManager dnsResoverManager) {
        this.mDnsResoverManager = dnsResoverManager;
        this.mInvocationHandler = newInvocationHandler();
    }

    private RealInvocationHandler newInvocationHandler() {
        try {
            return new RealInvocationHandler();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public Object createProxyInstance(Object realImpl) {
        try {
            Class<?>[] interfaces = realImpl.getClass().getInterfaces();
            return Proxy.newProxyInstance(DnsInetAddress.class.getClassLoader(), interfaces, this);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        }catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public void hook() {
        try {
            if (mInvocationHandler == null) {
                return;
            }
            Field addressImplField = mInvocationHandler.getAddressImplField();
            if (addressImplField == null) {
                return;
            }
            Object proxyInstance = createProxyInstance(mInvocationHandler.getAddressImpl());
            if (proxyInstance == null) {
                return;
            }
            addressImplField.set(InetAddress.class, proxyInstance);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        boolean shouldHookProxy = mDnsResoverManager.shouldHookProxy(method, args);
        if (shouldHookProxy) {
            Object result = mDnsResoverManager.lookup(method.getName(), method.getParameterTypes(), args);
            if (result != null) {
                return result;
            }
        }
        return mInvocationHandler.invoke(proxy, method, args);
    }

    static class RealInvocationHandler implements InvocationHandler {
        private Object mAddressImpl_instance;
        private Field addressImplField;

        public RealInvocationHandler() throws NoSuchFieldException, IllegalAccessException {
            this.addressImplField = getInetAddressImplField();
            if (addressImplField != null) {
                this.mAddressImpl_instance = addressImplField.get(null);
            }
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable  {
            try {
                Object result = method.invoke(mAddressImpl_instance, args);
                return result;
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }catch (Exception e){
                throw  e;
            }
            return null;
        }
        public Object getAddressImpl() {
            return mAddressImpl_instance;
        }
        public Field getAddressImplField() {
            return addressImplField;
        }
    }
    public static Field getInetAddressImplField() throws NoSuchFieldException, IllegalAccessException {
        Field InetAddress_impl = InetAddress.class.getDeclaredField("impl");
        InetAddress_impl.setAccessible(true);
        return InetAddress_impl;
    }
}
